package se.mbaeumer.fxtodo.gui;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.stream.XMLStreamException;

import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.ValidationException;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import se.mbaeumer.fxmessagebox.MessageBox;
import se.mbaeumer.fxmessagebox.MessageBoxResult;
import se.mbaeumer.fxmessagebox.MessageBoxType;
import se.mbaeumer.fxtodo.core.main.FXTodoMain;
import se.mbaeumer.fxtodo.core.model.Context;
import se.mbaeumer.fxtodo.core.model.EnumICSInfoType;
import se.mbaeumer.fxtodo.core.model.EnumPriority;
import se.mbaeumer.fxtodo.core.model.EnumTaskStatus;
import se.mbaeumer.fxtodo.core.model.Task;
import se.mbaeumer.fxtodo.core.model.TaskStatus;
import se.mbaeumer.fxtodo.core.util.constants.ContextConstants;
import se.mbaeumer.fxtodo.core.util.constants.FilterConstants;
import se.mbaeumer.fxtodo.core.util.dataexport.ICSWriter;
import se.mbaeumer.fxtodo.core.util.dataexport.XMLWriter;
//import se.mbaeumer.fxtodo.core.model.TaskStatus;
//import se.mbaeumer.fxtodo.core.model.TaskType;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.SortType;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.FileChooser;

public class FXTodo extends Application {

	private Group root = new Group();
	private Scene scene;
	private FlowPane flowGeneral;
	private FlowPane flowContext;
	private FlowPane flowItem;
	private FlowPane flowTask;
	private ComboBox<String> cmbItems;
	private ComboBox<String> cmbTaskViews;
	private ComboBox<String> cmbContexts;
	private ComboBox<String> cmbStatuses;
	private ComboBox<String> cmbPriorities;
	
	private TableView<Context> tblContexts;
	private TableView<Task> tblTasks;
	//private TableView<TaskType> tblTaskTypes;
	//private TableView<TaskStatus> tblTaskStatuses;
	private Label lblContexts;
	private Label lblItems;
	private Label lblTimeFilter;
	private Label lblStatusFilter;
	private Label lblPriorityFilter;
	//private Label lblTableView;

	private javafx.scene.control.MenuBar menuBar;
	private Menu menuExport;
	private Menu menuImport;
	private MenuItem miExportXML;
	private MenuItem miImportXML;
	private Button btnResetFilter;
	
	private DateStage dateStage;	
	private ContextMenu contTask;
	private ContextMenu contContext;
	
	private Task selectedTask;
	private Context selectedContext; 

	private FXTodoMain fxtodoMain = FXTodoMain.getInstance();

	public void start(Stage stage) {
		this.scene = new Scene(this.root, 1000, 700, Color.BEIGE);

		// set the stage
		stage.setTitle("FX Todo");
		// stage.getIcons().add(arg0);
		stage.setScene(this.scene);
		stage.show();
		this.initLayout();
		this.cmbItems.setValue("Task");
		this.contTask = new ContextMenu();
		this.contContext = new ContextMenu();
	}

	public static void main(String[] args) {
		launch(args);
	}

	public void initLayout() {
		// create the general flow pane
		this.createGeneralFlowPane();
		this.createMenu();
		this.createItemFlowPane();
		this.createTaskFlowPane();
		
		// create the table views
		this.createContextTableView();
		this.createTaskTableView();
	}
	
	public void createMenu(){
		this.createMenuBar();
		this.createExportMenu();
		this.createExportMenuItems();
		this.createImportMenu();
		this.createImportMenuItems();
	}
	
	public void createMenuBar(){
		this.menuBar = new MenuBar();
		this.flowGeneral.getChildren().add(this.menuBar);
	}
	
	public void createExportMenu(){
		this.menuExport = new Menu("Export");
		this.menuBar.getMenus().add(menuExport);
	}
	
	public void createExportMenuItems(){
		this.miExportXML = new MenuItem("Export to XML");
		this.miExportXML.setOnAction(new EventHandler<ActionEvent>() {
			
			@Override
			public void handle(ActionEvent arg0) {
				FileChooser fc = new FileChooser();
				fc.getExtensionFilters().add(
						new ExtensionFilter("xml", "*.xml"));
				File exportFile = fc.showSaveDialog(null);
				if (exportFile == null){
					arg0.consume();
					return;
				}
				
				try {
					XMLWriter xmlWriter = new XMLWriter(fxtodoMain, exportFile.getCanonicalPath());
					xmlWriter.writeDataToFile();
					xmlWriter = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		this.menuExport.getItems().add(miExportXML);
	}
	
	private void createImportMenu(){
		this.menuImport = new Menu("Import");
		this.menuBar.getMenus().add(this.menuImport);
	}
	
	private void createImportMenuItems(){
		this.miImportXML = new MenuItem("Import from XML");
		this.miImportXML.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				FileChooser fc = new FileChooser();
				fc.getExtensionFilters().add(
						new ExtensionFilter("xml", "*.xml"));
				File importFile= fc.showOpenDialog(null);
				if (importFile == null){
					arg0.consume();
					return;
				}
				
				try {
					MessageBox mb = new MessageBox("All entries will be replaced by the import. Proceed?", MessageBoxType.YES_NO);
					mb.showAndWait();
					if (mb.getMessageBoxResult() == MessageBoxResult.YES){
						fxtodoMain.importFromXML(importFile.getCanonicalPath());
						setAvailableContexts();
						cmbItems.setValue("Task");
						
						//cmbContexts.setValue(ContextConstants.CONTEXT_ALL);						
						tblTasks.setItems(FXCollections.observableList(
								fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
						tblTasks.getItems().add(fxtodoMain.getDummyTask());
					}
				} catch (XMLStreamException | IOException e) {
					e.printStackTrace();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		});
		
		MenuItem miImportICS = new MenuItem("Import from ICS");
		miImportICS.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				FileChooser fc = new FileChooser();
				fc.getExtensionFilters().add(
						new ExtensionFilter("ics", "*.ics"));
				File importFile= fc.showOpenDialog(null);
				if (importFile == null){
					arg0.consume();
					return;
				}
				
				try {
					fxtodoMain.importTaskFromICS(importFile.getCanonicalPath());
					setAvailableContexts();
					cmbItems.setValue("Task");
					tblTasks.setItems(FXCollections.observableList(
							fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
					tblTasks.getItems().add(fxtodoMain.getDummyTask());
				} catch (IOException e) {
					e.printStackTrace();
				} catch (ParserException e) {
					e.printStackTrace();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		});

		this.menuImport.getItems().addAll(this.miImportXML, miImportICS);
	}

	public void createGeneralFlowPane() {
		this.flowGeneral = new FlowPane();
		this.flowGeneral.setOrientation(Orientation.VERTICAL);
		this.flowGeneral.setPrefWrapLength(700);
		this.flowGeneral.setVgap(10);
		this.root.getChildren().add(this.flowGeneral);
	}
	
	public void createContextFlowPane(){
		this.flowContext = new FlowPane();
		this.flowContext.setOrientation(Orientation.HORIZONTAL);
		this.flowContext.setHgap(10);
		this.flowGeneral.getChildren().add(this.flowContext);
		this.createContextLabel();
		this.createContextComboBox();
	}
	
	public void createItemFlowPane(){
		this.flowItem = new FlowPane();
		this.flowItem.setOrientation(Orientation.HORIZONTAL);
		this.flowItem.setHgap(10);
		this.flowGeneral.getChildren().add(this.flowItem);
		this.createItemLabel();
		this.createItemComboBox();
	}
	
	public void createItemLabel(){
		this.lblItems = new Label("Items");
		this.flowItem.getChildren().add(this.lblItems);
	}
	
	/**
	 * Creates the options for the items
	 */
	public void createItemComboBox() {
		this.cmbItems = new ComboBox<String>();
		List<String> items = new ArrayList<String>();
		items.add("Context");
		items.add("Task");
		this.cmbItems.setItems(FXCollections.observableArrayList(items));
		this.cmbItems.valueProperty().addListener(
				new ChangeListener<String>(){
					@SuppressWarnings("rawtypes")
					@Override
					public void changed(ObservableValue ov, String s1, String s2){
						cmbContexts.setDisable(true);
						cmbTaskViews.setDisable(true);
						cmbStatuses.setDisable(true);
						cmbPriorities.setDisable(true);
						btnResetFilter.setDisable(true);
						if (cmbItems.getValue().equals("Task")){
							cmbContexts.setDisable(false);
							cmbTaskViews.setDisable(false);
							cmbStatuses.setDisable(false);
							cmbPriorities.setDisable(false);
							btnResetFilter.setDisable(false);
							resetTaskSorting(tblTasks);
						}else{
							resetTaskSorting(tblContexts);
						}
						switchTableView(cmbItems.getValue());
					}
				});

		this.flowItem.getChildren().add(this.cmbItems);
	}
	
	public void createTaskFlowPane(){
		this.flowTask = new FlowPane();
		this.flowTask.setOrientation(Orientation.HORIZONTAL);
		this.flowTask.setHgap(10);
		this.flowGeneral.getChildren().add(this.flowTask);
		this.createContextLabel();
		this.createContextComboBox();
		this.createTimeFilterLabel();
		this.createTaskViewComboBox();
		this.createStatusFilterLabel();
		this.createTaskStatusComboBox();
		this.createPriorityFilterLabel();
		this.createPriorityComboBox();
		this.createResetButton();
	}
	
	public void createContextLabel(){
		this.lblContexts = new Label("Context filter");
		this.flowTask.getChildren().add(this.lblContexts);
	}
	
	public void createContextComboBox() {
		this.cmbContexts = new ComboBox<String>();
		this.setAvailableContexts();
		this.cmbContexts.valueProperty().addListener(
				new ChangeListener<String>(){
					@SuppressWarnings("rawtypes")
					@Override
					public void changed(ObservableValue ov, String s1, String s2){
						if (cmbContexts.isFocused()){
							tblTasks.setItems(FXCollections.observableList(
									fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
							tblTasks.getItems().add(fxtodoMain.getDummyTask());
							resetTaskSorting(tblTasks);
						}
					}
				}
				);
		this.flowTask.getChildren().add(this.cmbContexts);
	}
	
	public void createTimeFilterLabel(){
		this.lblTimeFilter = new Label("Time filter");
		this.flowTask.getChildren().add(this.lblTimeFilter);
	}	
	
	/**
	 * Creates the options for the task view
	 */
	public void createTaskViewComboBox() {
		this.cmbTaskViews = new ComboBox<String>();
		List<String> items = new ArrayList<String>();
		
		items.add(FilterConstants.FILTER_NONE);
		items.add(FilterConstants.FILTER_TODAY);
		items.add(FilterConstants.FILTER_TOMORROW);
		items.add(FilterConstants.FILTER_WEEKLY);
		items.add(FilterConstants.FILTER_MONTHLY);
		
		this.cmbTaskViews.setItems(FXCollections.observableArrayList(items));
		this.cmbTaskViews.setValue("None");
		this.cmbTaskViews.valueProperty().addListener(
				new ChangeListener<String>(){
					@SuppressWarnings("rawtypes")
					@Override
					public void changed(ObservableValue ov, String s1, String s2){
						List<Task> tasks = fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue());
						tblTasks.setItems(FXCollections.observableList(tasks));
						tblTasks.getItems().add(fxtodoMain.getDummyTask());
						resetTaskSorting(tblTasks);
					}
				}
				);
		this.flowTask.getChildren().add(this.cmbTaskViews);
	}
	
	private void createStatusFilterLabel(){
		this.lblStatusFilter = new Label("Status filter");
		this.flowTask.getChildren().add(this.lblStatusFilter);
	}
	
	private void createTaskStatusComboBox(){
		// init
		this.cmbStatuses = new ComboBox<String>();
		this.cmbStatuses.getItems().add(FilterConstants.FILTER_NONE);
		for (TaskStatus status : this.fxtodoMain.getTaskStatuses()){
			this.cmbStatuses.getItems().add(status.getTitle());
		}
		
		this.cmbStatuses.setValue(FilterConstants.FILTER_NONE);
		
		// event handler
		this.cmbStatuses.valueProperty().addListener(
				new ChangeListener<String>(){
					@SuppressWarnings("rawtypes")
					@Override
					public void changed(ObservableValue ov, String s1, String s2){
						if (cmbStatuses.isFocused()){
							List<Task> tasks = fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue());
							tblTasks.setItems(FXCollections.observableList(tasks));
							tblTasks.getItems().add(fxtodoMain.getDummyTask());
							resetTaskSorting(tblTasks);
						}
					}
				});
		
		// add to flow pane
		this.flowTask.getChildren().add(this.cmbStatuses);
	}
	
	public void createPriorityFilterLabel(){
		this.lblPriorityFilter = new Label("Priority filter");
		this.flowTask.getChildren().add(this.lblPriorityFilter);
	}
	
	public void createPriorityComboBox(){
		this.cmbPriorities = new ComboBox<String>();
		this.cmbPriorities.getItems().add(FilterConstants.FILTER_NONE);
		this.cmbPriorities.getItems().addAll(this.fxtodoMain.getPriorityTitles());
		this.cmbPriorities.setValue(FilterConstants.FILTER_NONE);
		this.cmbPriorities.valueProperty().addListener(
				new ChangeListener<String>(){
					@SuppressWarnings("rawtypes")
					@Override
					public void changed(ObservableValue ov, String s1, String s2){
						if (cmbPriorities.isFocused()){
							List<Task> tasks = fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue());
							tblTasks.setItems(FXCollections.observableList(tasks));
							tblTasks.getItems().add(fxtodoMain.getDummyTask());
							resetTaskSorting(tblTasks);
						}
					}
				});
		this.flowTask.getChildren().add(this.cmbPriorities);
	}
	
	private void createResetButton(){
		this.btnResetFilter = new Button("Reset filters");
		this.btnResetFilter.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				cmbContexts.setValue(ContextConstants.CONTEXT_ALL);
				cmbTaskViews.setValue(FilterConstants.FILTER_NONE);
				cmbStatuses.setValue(FilterConstants.FILTER_NONE);
				cmbPriorities.setValue(FilterConstants.FILTER_NONE);
				
				tblTasks.setItems(FXCollections.observableList(fxtodoMain.getAllTasks()));
				tblTasks.getItems().add(fxtodoMain.getDummyTask());
				
			}
		
		});
		this.flowTask.getChildren().add(this.btnResetFilter);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	/**
	 * Creates the table for the contexts
	 */
	public void createContextTableView() {
		// create the tableview itself
		this.tblContexts = new TableView<Context>();
		this.tblContexts.setEditable(true);
		
		// bind the contexts to the table view
		ObservableList<Context> observableContexts =
				FXCollections.observableList(this.fxtodoMain.getAllContexts());
		
		this.tblContexts.setItems(observableContexts);
		this.tblContexts.getItems().add(this.fxtodoMain.getDummyContext());
		/*this.tblContexts.getItems().addListener(
				new ListChangeListener(){
					@Override
					public void onChanged(ListChangeListener.Change change){
						//System.out.println("changed");
						List<Context> news=change.getAddedSubList();
					}
				});
		*/
		this.tblContexts.prefWidthProperty().bind(this.scene.widthProperty());
		this.tblContexts.prefWidthProperty().addListener(new ChangeListener() {
			@Override
			public void changed(ObservableValue ov, Object oldValue, Object newValue){
				setContextTableLayout();
			}
		
		});
		// create the columns
		this.createContextTableColumns();
		this.setContextTableLayout();
		
		// add the table to the graph
		this.flowGeneral.getChildren().add(this.tblContexts);
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	/**
	 * Creates the columns for the context table
	 */
	public void createContextTableColumns(){
		// create title column
		TableColumn titleCol = new TableColumn("Title");
		titleCol.setCellValueFactory(new PropertyValueFactory("title"));
		titleCol.setCellFactory(TextFieldTableCell.forTableColumn());
		titleCol.setOnEditCommit(
		    new EventHandler<CellEditEvent<Context, String>>() {
		        public void handle(CellEditEvent<Context, String> t) {
		            String old = t.getOldValue();
		        	((Context) t.getTableView().getItems().get(
		                t.getTablePosition().getRow())
		                ).setTitle(t.getNewValue());
		            if (!validateContextTitle(t.getNewValue())){
		            	((Context) t.getTableView().getItems().get(
				                t.getTablePosition().getRow())
				                ).setTitle(old);
		            	return;
		            }
		            refreshContextTable(t.getRowValue());
		            resetTaskSorting(tblContexts);
		        }
		    }
		);

		// create description column
		TableColumn descriptionCol = new TableColumn("Description");
		descriptionCol.setCellValueFactory(new PropertyValueFactory("description"));
		descriptionCol.setCellFactory(TextFieldTableCell.forTableColumn());
		descriptionCol.setOnEditCommit(
		    new EventHandler<CellEditEvent<Context, String>>() {
		        public void handle(CellEditEvent<Context, String> t) {
		            ((Context) t.getTableView().getItems().get(
		                t.getTablePosition().getRow())
		                ).setDescription(t.getNewValue());
		            
		            refreshContextTable(t.getRowValue());
		            resetTaskSorting(tblContexts);
		        }
		    }
		);
		
		// create the created column
		TableColumn createdCol = new TableColumn("Created");
		createdCol.setCellValueFactory(new PropertyValueFactory("createdString"));
		
		tblContexts.addEventHandler(MouseEvent.MOUSE_CLICKED, 
				new EventHandler<MouseEvent>() {

					@Override
					public void handle(MouseEvent me) {
						contContext.hide();
						if (me.getButton() == MouseButton.SECONDARY){
							
							selectedContext = tblContexts.getSelectionModel().getSelectedItem();
							if (selectedContext != null){
								createContextContextMenu();
		 						contContext.show(tblContexts, me.getScreenX(), me.getSceneY());
							}
						}
					}
				});

		// add all columns to the table view
		this.tblContexts.getColumns().addAll(titleCol, descriptionCol, createdCol);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	/**
	 * Creates the task table
	 */
	public void createTaskTableView() {
		// create the table view itself
		this.tblTasks = new TableView();
	
		this.tblTasks.prefWidthProperty().bind(this.scene.widthProperty());
		this.tblTasks.prefWidthProperty().addListener(new ChangeListener() {
			@Override
			public void changed(ObservableValue ov, Object oldValue, Object newValue){
				setTaskTableLayout();
			}
		});
		// bind the tasks to the table view
		this.tblTasks.setItems(FXCollections.observableList(this.fxtodoMain.getAllTasks()));
		this.tblTasks.getItems().add(this.fxtodoMain.getDummyTask());
		// create the columns
		this.createTaskTableColumns();
		this.tblTasks.setEditable(true);
		this.setTaskTableLayout();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	/**
	 * Creates the columns for the task table
	 */
	public void createTaskTableColumns(){
		// create the title column
		TableColumn titleCol = new TableColumn("Title");
		titleCol.setCellValueFactory(new PropertyValueFactory("title"));
		titleCol.setCellFactory(TextFieldTableCell.forTableColumn());
		titleCol.setOnEditCommit(
		    new EventHandler<CellEditEvent<Task, String>>() {
		        public void handle(CellEditEvent<Task, String> t) {
		            ((Task) t.getTableView().getItems().get(
		                t.getTablePosition().getRow())
		                ).setTitle(t.getNewValue());
		            refreshTaskTable(t.getRowValue());
		            resetTaskSorting(tblTasks);
		        }
		    }
		);

		// create description column
		TableColumn descriptionCol = new TableColumn("Description");
		descriptionCol.setCellValueFactory(new PropertyValueFactory("description"));
		descriptionCol.setCellFactory(TextFieldTableCell.forTableColumn());
		descriptionCol.setOnEditCommit(
		    new EventHandler<CellEditEvent<Task, String>>() {
		        public void handle(CellEditEvent<Task, String> t) {
		            ((Task) t.getTableView().getItems().get(
		                t.getTablePosition().getRow())
		                ).setDescription(t.getNewValue());
		            refreshTaskTable(t.getRowValue());
		            resetTaskSorting(tblTasks);
		        }
		    }
		);
		
		// create the created column
		TableColumn<Task,String> createdCol = new TableColumn<Task,String>("Created");
		createdCol.setCellValueFactory(new PropertyValueFactory<Task, String>("createdString"));

		// create the dueDate column
		TableColumn dueDateCol = new TableColumn("Due date");
		dueDateCol.setCellValueFactory(new PropertyValueFactory<Task, String>("dueDateString"));
		
		tblTasks.addEventHandler(MouseEvent.MOUSE_CLICKED, 
				new EventHandler<MouseEvent>() {

					@Override
					public void handle(MouseEvent me) {
						contTask.hide();
						if (me.getButton() == MouseButton.SECONDARY){
							
							selectedTask = tblTasks.getSelectionModel().getSelectedItem();
							if (selectedTask != null){
								createTaskContextMenu();
								contTask.show(tblTasks, me.getScreenX(), me.getSceneY());
							}
						}
					}
				});
		
		// create the status column
		TableColumn statusCol = new TableColumn("Status");
		statusCol.setCellValueFactory(new PropertyValueFactory("status"));
		statusCol.setCellFactory(ComboBoxTableCell.forTableColumn(this.createStatuses()));
		statusCol.setOnEditCommit(
			    new EventHandler<CellEditEvent<Task,EnumTaskStatus>>() {
			        public void handle(CellEditEvent<Task,EnumTaskStatus> t) {
			            ((Task) t.getTableView().getItems().get(
			                t.getTablePosition().getRow())
			                ).setStatus(t.getNewValue());
			            refreshTaskTable(t.getRowValue());
			            resetTaskSorting(tblTasks);
			        }
			    }
			);
		
		TableColumn priorityCol = new TableColumn("Priority");
		priorityCol.setCellValueFactory(new PropertyValueFactory("priority"));
		priorityCol.setCellFactory(ComboBoxTableCell.forTableColumn(this.createPriorities()));
		priorityCol.setOnEditCommit(
			    new EventHandler<CellEditEvent<Task,EnumPriority>>() {
			        public void handle(CellEditEvent<Task,EnumPriority> t) {
			            ((Task) t.getTableView().getItems().get(
			                t.getTablePosition().getRow())
			                ).setPriority(t.getNewValue());
			            refreshTaskTable(t.getRowValue());
			            resetTaskSorting(tblTasks);
			        }
			    }
			);
		/*TODO: The column taskType is used in the next release*/
		// create the task type column
		/*TableColumn typeCol = new TableColumn("Type");
		typeCol.setCellValueFactory(new PropertyValueFactory("taskType"));
	*/
		// add all columns to the table view
		this.tblTasks.getColumns().addAll(titleCol, descriptionCol, 
				dueDateCol, statusCol, priorityCol, createdCol);
	}
	
	/**
	 * Switches the table views
	 * @param item the item, i.e. context or task
	 */
	public void switchTableView(String item){
		if (item.equals("Context")){
			this.flowGeneral.getChildren().remove(this.tblTasks);
			this.flowGeneral.getChildren().add(this.tblContexts);
			tblContexts.setItems(FXCollections.observableList(fxtodoMain.getAllContexts()));
			tblContexts.getItems().add(fxtodoMain.getDummyContext());
		}else if (item.equals("Task")){
			this.flowGeneral.getChildren().remove(this.tblContexts);
			this.flowGeneral.getChildren().add(this.tblTasks);
			tblTasks.setItems(FXCollections.observableList(
					fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
			tblTasks.getItems().add(fxtodoMain.getDummyTask());
		}
	}
	
	/**
	 * Create a list of task statuses that is added to the status column
	 * in the table view
	 * @return the status list
	 */
	public ObservableList<EnumTaskStatus> createStatuses(){
		EnumTaskStatus[] statuses = EnumTaskStatus.values();
		List<EnumTaskStatus> statusList = new ArrayList<EnumTaskStatus>();
		for (int i=0;i<EnumTaskStatus.values().length;i++){
			statusList.add(statuses[i]);
		}
		return FXCollections.observableList(statusList);
	}
	
	public ObservableList<EnumPriority> createPriorities(){
		EnumPriority[] priorities = EnumPriority.values();
		List<EnumPriority> priorityList = new ArrayList<EnumPriority>();
		for (int i=0;i<EnumPriority.values().length;i++){
			priorityList.add(priorities[i]);
		}
		return FXCollections.observableList(priorityList);
	}
	
	@SuppressWarnings("rawtypes")
	/**
	 * Sets the layout of the task table
	 */
	private void setTaskTableLayout(){
		for (Object o : this.tblTasks.getColumns()){
			TableColumn tc = (TableColumn) o;
			tc.setPrefWidth((this.tblTasks.getPrefWidth()*20)/100);
		}
	}
		
	@SuppressWarnings("rawtypes")
	/**
	 * Sets the layout of the context table
	 */
	private void setContextTableLayout(){
		for (Object o : this.tblContexts.getColumns()){
			TableColumn tc = (TableColumn) o;
			if (tc.getText().equals("")){
				tc.setVisible(false);
			}else if (tc.getText().equals("Title")){
				tc.setPrefWidth((this.tblContexts.getPrefWidth()*30)/100);
			}else if (tc.getText().equals("Description")){
				tc.setPrefWidth((this.tblContexts.getPrefWidth()*40)/100);
			}else if (tc.getText().equals("Created")){
				tc.setPrefWidth((this.tblContexts.getPrefWidth()*30)/100);
			}
		}
	}
	
	private void createTaskContextMenu(){
		this.contTask = new ContextMenu();
		
		MenuItem miEdit = new MenuItem("Edit the due date of the currently selected task");
		miEdit.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent e) {
				Date before = selectedTask.getDueDate();
				dateStage = new DateStage(selectedTask);
				dateStage.initModality(Modality.APPLICATION_MODAL);
				dateStage.showAndWait();
				selectedTask = dateStage.getTask(); 
				Date after = selectedTask.getDueDate();
				if (!before.equals(after)){
					fxtodoMain.updateTask(selectedTask, cmbContexts.getValue());
					refreshTaskTable(selectedTask);
					resetTaskSorting(tblTasks);
				}
			}
		});
		
		MenuItem miMove = new MenuItem("Move task");
		if (this.selectedTask.getId() == -1){
			miMove.setDisable(true);
		}else{
			miMove.setDisable(false);
		}
		
		miMove.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent e) {
				TaskMoveStage taskMoveStage = new TaskMoveStage(fxtodoMain, selectedTask);
				taskMoveStage.showAndWait();
				tblTasks.setItems(FXCollections.observableList(
						fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
				tblTasks.getItems().add(fxtodoMain.getDummyTask());
			}
		});
		
		MenuItem miDelete = new MenuItem("Delete task");
		if (this.selectedTask.getId() == -1){
			miDelete.setDisable(true);
		}else{
			miDelete.setDisable(false);
		}
		
		miDelete.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent e) {
				MessageBox mb = new MessageBox("Are you sure to delete this task?",MessageBoxType.YES_NO);
				mb.showAndWait();
				if (mb.getMessageBoxResult() == MessageBoxResult.YES){
					fxtodoMain.deleteTask(selectedTask.getId());
				}else{
					return;
				}				
				//tblTasks.setItems(FXCollections.observableList(fxtodoMain.getAllTasks()));
				tblTasks.setItems(FXCollections.observableList(
						fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
				tblTasks.getItems().add(fxtodoMain.getDummyTask());
			}
		});

		MenuItem miExportICSEvent = new MenuItem("Export to ICS as event");
		if (this.selectedTask.getId() == -1){
			miExportICSEvent.setDisable(true);
		}else{
			miExportICSEvent.setDisable(false);
		}
		
		miExportICSEvent.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent ae) {
				FileChooser fc = new FileChooser();
				fc.getExtensionFilters().add(
						new ExtensionFilter("ics", "*.ics"));
				File icsFile = fc.showSaveDialog(null);
				if (icsFile != null){
					try {
						ICSWriter icsWriter = new ICSWriter(icsFile.getCanonicalPath());
						icsWriter.writeToFile(selectedTask, EnumICSInfoType.Event);
					} catch (IOException | ParserException e1) {
						e1.printStackTrace();
					} catch (ValidationException e) {
						e.printStackTrace();
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		MenuItem miExportICSTodo = new MenuItem("Export to ICS as Todo");
		if (this.selectedTask.getId() == -1){
			miExportICSTodo.setDisable(true);
		}else{
			miExportICSTodo.setDisable(false);
		}
		
		miExportICSTodo.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent ae) {
				FileChooser fc = new FileChooser();
				fc.getExtensionFilters().add(
						new ExtensionFilter("ics", "*.ics"));
				File icsFile = fc.showSaveDialog(null);
				if (icsFile != null){
					try {
						ICSWriter icsWriter = new ICSWriter(icsFile.getCanonicalPath());
						icsWriter.writeToFile(selectedTask, EnumICSInfoType.Todo);
					} catch (IOException | ParserException e1) {
						e1.printStackTrace();
					} catch (ValidationException e) {
						e.printStackTrace();
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		MenuItem miViewICSInfo = new MenuItem("View ICS-info");
		if (this.selectedTask.getId() == -1
				|| this.fxtodoMain.getICSInfoByTaskID(this.selectedTask.getId()) == null){
			miViewICSInfo.setDisable(true);
		}else{
			miViewICSInfo.setDisable(false);
		}
		
		miViewICSInfo.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent ae) {
				ICSInfoStage icsInfoStage = 
						new ICSInfoStage(fxtodoMain.getICSInfoByTaskID(selectedTask.getId()));
				icsInfoStage.showAndWait();
			}
		});

		this.contTask.getItems().addAll(miEdit, miMove, miDelete, 
				miExportICSEvent, miViewICSInfo);
	}
	
	private void createContextContextMenu(){
		this.contContext = new ContextMenu();

		MenuItem miDeleteContextOnly = new MenuItem("Delete context only");
		if (this.selectedContext.getId() == -1){
			miDeleteContextOnly.setDisable(true);
		}else{
			miDeleteContextOnly.setDisable(false);
		}
		
		miDeleteContextOnly.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent e) {
				MessageBox mb = new MessageBox("Are you sure to delete this context?",MessageBoxType.YES_NO);
				mb.showAndWait();
				if (mb.getMessageBoxResult() == MessageBoxResult.YES){
					fxtodoMain.deleteContext(selectedContext.getId(), false);
				}else{
					return;
				}		
				tblContexts.setItems(FXCollections.observableList(fxtodoMain.getAllContexts()));
				tblContexts.getItems().add(fxtodoMain.getDummyContext());
				setAvailableContexts();
			}
		});
		
		MenuItem miDeleteContextCascading = new MenuItem("Delete context and tasks");
		if (this.selectedContext.getId() == -1){
			miDeleteContextCascading.setDisable(true);
		}else{
			miDeleteContextCascading.setDisable(false);
		}

		miDeleteContextCascading.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent e) {
				MessageBox mb = new MessageBox("Are you sure to delete this context and all tasks associated to it?",MessageBoxType.YES_NO);
				mb.showAndWait();
				if (mb.getMessageBoxResult() == MessageBoxResult.YES){
					fxtodoMain.deleteContext(selectedContext.getId(), true);
				}else{
					return;
				}		
				tblContexts.setItems(FXCollections.observableList(fxtodoMain.getAllContexts()));
				tblContexts.getItems().add(fxtodoMain.getDummyContext());
				setAvailableContexts();
			}
		});
		
		this.contContext.getItems().addAll(miDeleteContextCascading, miDeleteContextOnly);
	}
	
	private void refreshTaskTable(Task t){
		int id = -1; 
		if (t != null){
			id = t.getId(); 
		}
				
        fxtodoMain.updateTask(t, cmbContexts.getValue());
        tblTasks.setItems(FXCollections.
        		observableList(fxtodoMain.getTasks(cmbContexts.getValue(), cmbTaskViews.getValue(), cmbStatuses.getValue(), cmbPriorities.getValue())));
    	tblTasks.getItems().add(fxtodoMain.getDummyTask());
        /*if (id<0){
        	
        }*/
	}
	
	private void refreshContextTable(Context context){
		int id = context.getId();
        
		try {
			fxtodoMain.updateContext(context);
		} catch (SQLException e) {
			MessageBox mb = new MessageBox("Context must be unique!", MessageBoxType.OK_ONLY);
			mb.showAndWait();
			tblContexts.setItems(FXCollections.
            		observableList(fxtodoMain.getAllContexts()));
			tblContexts.getItems().add(fxtodoMain.getDummyContext());
			return;
		}
        
		tblContexts.setItems(FXCollections.
        		observableList(fxtodoMain.getAllContexts()));
    	tblContexts.getItems().add(fxtodoMain.getDummyContext());
        // if a new context has been added
        /*if (id<0){
        	
        }*/
        
        this.setAvailableContexts();
	}
	
	private boolean validateContextTitle(String title){

		if (title.equalsIgnoreCase(ContextConstants.CONTEXT_ALL)
				|| title.equalsIgnoreCase(ContextConstants.CONTEXT_UNKNOWN)){
			MessageBox mb = new MessageBox("Context must be unique!", MessageBoxType.OK_ONLY);
			mb.showAndWait();
			return false;
		}
		return true;
	}
	
	private void setAvailableContexts(){
		if (this.cmbContexts.getItems() != null){
			this.cmbContexts.getItems().clear();
		}
		this.cmbContexts.getItems().add(ContextConstants.CONTEXT_UNKNOWN);
		this.cmbContexts.getItems().add(ContextConstants.CONTEXT_ALL);
		this.cmbContexts.setValue(ContextConstants.CONTEXT_ALL);
		// load the contexts from the database
		this.cmbContexts.getItems().addAll(FXCollections.observableList(this.fxtodoMain.loadContextTitlesFromDB()));
	}
	
	private void resetTaskSorting(TableView<?> tblView){
		tblView.getSortOrder().clear();
	}
}
